﻿using System;
using System.Text;
using RabbitMQ.Client;
using RpcClient.Interface;
using RpcClient.Queue.Model;
using RpcClient.Rabbitmq.Interface;
using RpcClient.Rabbitmq.Model;
using RpcHelper;

namespace RpcClient.Rabbitmq
{
        [Attr.ClassLifetimeAttr (Attr.ClassLifetimeType.单例)]
        internal class RabbitmqService : IRabbitmqService
        {
                private static readonly Type _SubType = typeof (ISubscribe);
                private static readonly RabbitmqQueue _Rabbitmq = null;

                private static readonly MsgProperties _DefJson = null;
                private static readonly MsgProperties _DefText = null;
                static RabbitmqService ()
                {
                        RabbitmqConfig config = Config.WebConfig.GetRabbitmqConfig ();
                        _Rabbitmq = new RabbitmqQueue (config);
                        _DefJson = new MsgProperties
                        {
                                ContentEncoding = "utf-8",
                                Expiration = config.Expiration,
                                ContentType = "application/json",
                                IsLasting = config.IsLasting
                        };
                        _DefText = new MsgProperties
                        {
                                ContentEncoding = "utf-8",
                                Expiration = config.Expiration,
                                ContentType = "text/plain",
                                IsLasting = config.IsLasting
                        };
                }

                /// <summary>
                /// 发布消息
                /// </summary>
                /// <typeparam name="T"></typeparam>
                /// <param name="msg"></param>
                /// <param name="exchange"></param>
                /// <param name="routeKey"></param>
                public void Public<T> (T msg, string exchange, params string[] routeKey) where T : class
                {
                        this._Public (msg.ToJson (), exchange, _DefJson, routeKey);
                }
                public void Public<T> (T msg, string exchange, MsgProperties properties, params string[] routeKey) where T : class
                {
                        properties.ContentType = "application/json";
                        this._Public (msg.ToJson (), exchange, _DefJson, routeKey);
                }
                public void Public (string msg, string exchange, params string[] routeKey)
                {
                        this._Public (msg, exchange, _DefText, routeKey);
                }
                public void Public (string msg, string exchange, MsgProperties properties, params string[] routeKey)
                {
                        properties.ContentType = "text/plain";
                        this._Public (msg, exchange, properties, routeKey);
                }
                private void _Public (string msg, string exchange, MsgProperties properties, params string[] routeKey)
                {
                        using (IModel channel = _Rabbitmq.CreateChannel ())
                        {
                                IBasicProperties props = _Rabbitmq.GetProperties (channel, properties);
                                byte[] datas = Encoding.UTF8.GetBytes (msg);
                                routeKey.ForEach (a =>
                                 {
                                         channel.BasicPublish (exchange, a, false, props, datas);
                                 });
                        }
                }

                private bool _PublicTran (string msg, string exchange, MsgProperties properties, params string[] routeKey)
                {
                        using (IModel channel = _Rabbitmq.CreateChannel ())
                        {
                                IBasicProperties props = _Rabbitmq.GetProperties (channel, properties);
                                byte[] datas = Encoding.UTF8.GetBytes (msg);
                                channel.ConfirmSelect ();
                                routeKey.ForEach (a =>
                                 {
                                         channel.BasicPublish (exchange, a, false, props, datas);
                                 });
                                try
                                {
                                        channel.WaitForConfirmsOrDie ();
                                }
                                catch (Exception e)
                                {
                                        ErrorException error = ErrorException.FormatError (e);
                                        error.SaveLog ("Rabbitmq");
                                        return false;
                                }
                                return true;
                        }
                }

                public void PublicTran<T> (T msg, string exchange, params string[] routeKey) where T : class
                {
                        this._PublicTran (msg.ToJson (), exchange, _DefJson, routeKey);
                }
                public void PublicTran<T> (T msg, string exchange, MsgProperties properties, params string[] routeKey) where T : class
                {
                        properties.ContentType = "application/json";
                        this._PublicTran (msg.ToJson (), exchange, _DefJson, routeKey);
                }
                public void PublicTran (string msg, string exchange, params string[] routeKey)
                {
                        this._PublicTran (msg, exchange, _DefText, routeKey);
                }
                public void PublicTran (string msg, string exchange, MsgProperties properties, params string[] routeKey)
                {
                        properties.ContentType = "text/plain";
                        this._PublicTran (msg, exchange, properties, routeKey);
                }

                public ISubscribe Subscribe (string exchange, Action<ISubscribe, ISubEventArgs> action, bool isAutoAck = true, string exchangeType = "direct")
                {
                        if (RpcClient.Unity.IsRegistered (_SubType, exchange))
                        {
                                return RpcClient.Unity.Resolve<ISubscribe> (exchange);
                        }
                        Subscribe sub = new Subscribe (_Rabbitmq, this, exchange, isAutoAck, exchangeType, action);
                        if (RpcClient.Unity.RegisterInstance<ISubscribe> (sub, exchange))
                        {
                                sub.Init ();
                                return sub;
                        }
                        return RpcClient.Unity.Resolve<ISubscribe> (exchange);
                }
        }
}
